//
// Created by 86155 on 2025/4/16.
//
#include "llvm/CodeGen/SelectionDAGISel.h"
#include "Fe.h"
#include "MCTargetDesc/FeMCTargetDesc.h"
#include "FeSubtarget.h"
#include "FeTargetMachine.h"
#include "MCTargetDesc/FeMCTargetDesc.h"
#include "FeDataCtrl.h"

using namespace llvm;
#define DEBUG_TYPE "fe-isel"
#define PASS_NAME "Fe DAG->DAG Pattern Instruction Selection"
//第一个子类
class FeDAGToDAGISel : public SelectionDAGISel {
public:
  FeDAGToDAGISel() = delete;
  explicit FeDAGToDAGISel(FeTargetMachine &TM, CodeGenOptLevel OL/*优化级别*/)
      : SelectionDAGISel(TM,OL), Subtarget(nullptr) {}

  bool runOnMachineFunction(MachineFunction &MF) override;  /*入口函数*/

  bool SelectAddrFI(SDNode *Parent, SDValue AddrFI, SDValue &BASE, SDValue &Offset);

private:
  const FeSubtarget *Subtarget;

#include "FeGenDAGISel.inc"

  const FeTargetMachine &getTargetMachine() {
    return static_cast<const FeTargetMachine &>(TM);
  }

  bool isJALTarget(SDValue Target);
  void Select(SDNode *N) override;
};

bool FeDAGToDAGISel::runOnMachineFunction(MachineFunction &MF) {
  Subtarget = &MF.getSubtarget<FeSubtarget>();
  return SelectionDAGISel::runOnMachineFunction(MF);  /*调用基类*/
}

bool FeDAGToDAGISel::isJALTarget(SDValue Target) {
  // 检查目标是否符合JAL的条件
  // 例如：目标是否在JAL的寻址范围内

  return true; // 自定义判断逻辑
}

void FeDAGToDAGISel::Select(SDNode *Node) {
  if (Node->isMachineOpcode()) {
    Node->setNodeId(-1);
    return;
  }
  SDLoc DL(Node);

  switch (Node->getOpcode()) {
//  case ISD::BR: {
//    SDValue Target = Node->getOperand(0);
//    SDValue Targetadd = Node->getOperand(1);
//
//    Target.getNode()->dump();
//    Targetadd.getNode()->dump();
//
//    if (isJALTarget(Target)) {
//      printf("Found BR that matches JAL pattern\n");
//    }
//  }
    //1. DIN/DOUT指令的实现   √
    //2. 创建一个类实现缓存内的数据块搬移 mem inbuffer outbuffer √
    case FeISD::Call:{
      SmallVector<SDValue> Ops;
      for (unsigned i = 1; i < Node->getNumOperands(); ++i) {
        SDValue operand = Node->getOperand(i);
        Ops.push_back(operand);
      }

      SDValue globalAddress = Node->getOperand(1);
      GlobalAddressSDNode *GV = dyn_cast<GlobalAddressSDNode>(globalAddress);
      if(GV->getGlobal()->getName()=="DIN"){
        SDNode *Result =
            CurDAG->getMachineNode(Fe::DIN, DL, MVT::Other,MVT::Glue, Ops);
        ReplaceNode(Node, Result);
        return;
      }
      if(GV->getGlobal()->getName()=="DOUT"){
        SDNode *Result =
            CurDAG->getMachineNode(Fe::DOUT, DL, MVT::Other,MVT::Glue, Ops);
        ReplaceNode(Node, Result);
        return;
      }
      if(GV->getGlobal()->getName()=="RAC"){
        SDNode *Result =
            CurDAG->getMachineNode(Fe::RAC, DL, MVT::Other,MVT::Glue, Ops);
        ReplaceNode(Node, Result);
        return;
      }
      if(GV->getGlobal()->getName()=="NMCS"){
        SDNode *Result =
            CurDAG->getMachineNode(Fe::NMCS, DL, MVT::Other,MVT::Glue, Ops);
        ReplaceNode(Node, Result);
        return;
      }
      if(GV->getGlobal()->getName()=="NMTXD"){
        SDNode *Result =
            CurDAG->getMachineNode(Fe::NMTXD, DL, MVT::Other,MVT::Glue, Ops);
        ReplaceNode(Node, Result);
        return;
      }
      if(GV->getGlobal()->getName()=="Zero"){
        SDNode *Result =
            CurDAG->getMachineNode(Fe::Zero, DL, MVT::Other,MVT::Glue, Ops);
        ReplaceNode(Node, Result);
        return;
      }
      if(GV->getGlobal()->getName()=="SUM"){
        SDNode *Result =
            CurDAG->getMachineNode(Fe::SUM, DL, MVT::Other,MVT::Glue, Ops);
        ReplaceNode(Node, Result);
        return;
      }
      if(GV->getGlobal()->getName()=="Rea"){
        SDNode *Result =
            CurDAG->getMachineNode(Fe::Rea, DL, MVT::Other,MVT::Glue, Ops);
        ReplaceNode(Node, Result);
        return;
      }
      if(GV->getGlobal()->getName()=="ReadFE"){
        SDNode *Result =
            CurDAG->getMachineNode(Fe::ReadFE, DL, MVT::Other,MVT::Glue, Ops);
        ReplaceNode(Node, Result);
        return;
      }
      if(GV->getGlobal()->getName()=="CONF"){
        SDNode *Result =
            CurDAG->getMachineNode(Fe::CONF, DL, MVT::Other,MVT::Glue, Ops);
        ReplaceNode(Node, Result);
        return;
      }
    }

    case ISD::CopyToReg: {
      SmallVector<SDValue> Ops;
      for (unsigned i = 0; i < Node->getNumOperands(); ++i) {
        SDValue operand = Node->getOperand(i);
//        operand.dump();
        Ops.push_back(operand);
      }
    }
  }

//  LLVM_DEBUG(dbgs() << "Selecting: "; Node->dump(CurDAG); dbgs() << '\n');

  SelectCode(Node);/*交由tablegen实现*/
}

bool FeDAGToDAGISel::SelectAddrFI(SDNode *Parent, SDValue AddrFI, SDValue &Base, SDValue &Offset) {
  /// FrameIndex -> TargetFrameIndex
  /// FrameIndex -> TargetFrameIndex
  if (FrameIndexSDNode *FIN = dyn_cast<FrameIndexSDNode>(AddrFI)) {
    Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), AddrFI.getValueType());
    Offset = CurDAG->getTargetConstant(0, SDLoc(AddrFI), AddrFI.getValueType());
    return true;
  }
  /// 基地址+偏移地址
  if (CurDAG->isBaseWithConstantOffset(AddrFI)) {
    EVT ValTy = AddrFI.getValueType();
    ConstantSDNode *CN = dyn_cast<ConstantSDNode>(AddrFI.getOperand(1));
    /// 栈内基地址
    if (FrameIndexSDNode *FIN = dyn_cast<FrameIndexSDNode>(AddrFI.getOperand(0))) {
      Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), ValTy);
    }else {
      /// 全局变量基地址
      Base = AddrFI.getOperand(0);
    }
    Offset = CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(AddrFI), ValTy);
    return true;
  }
  return false;
}

/*第二个子类  PASS*/
class FeDAGToDAGISelLegacy : public SelectionDAGISelLegacy {
public:
  static char ID; /*pass ID*/
  explicit FeDAGToDAGISelLegacy(FeTargetMachine &TM)
      : SelectionDAGISelLegacy(ID, std::make_unique<FeDAGToDAGISel>(TM,TM.getOptLevel())) {}/*组合FeDAGToDAGISel*/
};

char FeDAGToDAGISelLegacy::ID;

INITIALIZE_PASS(FeDAGToDAGISelLegacy, DEBUG_TYPE, PASS_NAME, false, false)//注册FeDAGToDAGISelLegacy给llvm

FunctionPass *llvm::createFeISelDag(FeTargetMachine &TM) { //创建一个pass
  return new FeDAGToDAGISelLegacy(TM);
}